#include "CustomEffect.h"
#include <xnamath.h>
#include <fstream>
#include <sys/stat.h> 
//#include "afxres.h"
#include <D3D11.h>
#include <D3DX11.h>
#include <D3Dcompiler.h>
#include <D3DX11async.h>
#include <d3dx11effect.h>


// compiling shaders
// http://www.gamedev.net/topic/594395-hlsl-compile-in-visual-c/
// loading libs
// http://www.codeproject.com/KB/DLL/classesexportedusingLL.aspx

namespace Utils{

	char* GetResource(std::string instanceName, std::string name){
		char *pRet = NULL;    
		HMODULE hInstance = LoadLibrary(instanceName.c_str());
		HRSRC hRes = FindResourceA(hInstance, name.c_str(), "EFFECT");    
		if (hRes)    
		{        
			HGLOBAL hGlobal = LoadResource(hInstance, hRes);        
			char *pResource = (char*)LockResource(hGlobal);        
			if (pResource)        
			{            
				unsigned int uiSize = (unsigned int)SizeofResource(hInstance, hRes);            
				pRet = new char[uiSize + 1];            
				strncpy_s(pRet, uiSize + 1, pResource, uiSize);            
				pRet[uiSize] = '\0';            
				UnlockResource(pResource);        
			}        
			FreeResource(hGlobal);    
		}    
		return pRet;
	}

	bool FileExists(string strFilename) { 
		struct stat stFileInfo; 
		bool blnReturn; 
		int intStat; 

		// Attempt to get the file attributes	
		intStat = stat(strFilename.c_str(),&stFileInfo); 
		if(intStat == 0) { 
			// We were able to get the file attributes 
			// so the file obviously exists. 
			blnReturn = true; 
		} else { 
			// We were not able to get the file attributes. 
			// This may mean that we don't have permission to 
			// access the folder which contains this file. If you 
			// need to do that level of checking, lookup the 
			// return values of stat which will give you 
			// more details on why stat failed. 
			blnReturn = false; 
		} 

		return(blnReturn); 
	}
	
	CustomEffect::CustomEffect(string shaderName, string technique, int effectType, D3D11_INPUT_ELEMENT_DESC *layout, int layoutCount){
		Init(shaderName, technique, effectType, layout, layoutCount);
	}

	void CustomEffect::Init(string shaderName, string technique, int effectType, D3D11_INPUT_ELEMENT_DESC *layout, int layoutCount){

		texturesSet = false;
		effect = NULL;
		this->name = Globals::RES_PATH + shaderName;

		DWORD shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
		shaderFlags |= D3D10_SHADER_DEBUG;
		shaderFlags |= D3D10_SHADER_SKIP_OPTIMIZATION;
#endif

		HRESULT hr;
		
		if(FileExists(this->name)){		
			ifstream is(this->name,ios::out|ios::binary|ios::app); 
			is.seekg(0,ios_base::end); 
			streampos pos = is.tellg(); 
			is.seekg(0,ios_base::beg); 
			char * effectBuffer = new char[pos]; 
			is.read(effectBuffer,pos); 

			hr = D3DX11CreateEffectFromMemory((void *)effectBuffer,pos,shaderFlags,Globals::Device,&effect); 
		}
		else{
			std::string err = "Could not find " + this->name;
			MessageBoxA(0, err.c_str(), 0, 0);
			return;
		}

		if(FAILED(hr) ){
			MessageBoxA(0, "Could not create effect", 0, 0);
			return;
		}

		Technique = effect->GetTechniqueByName(technique.c_str());

		ERR(Technique->GetDesc( &techDesc ), "Technique " +technique+ " is not available");

		pVertexLayout = new ID3D11InputLayout*[ techDesc.Passes ];
		vertexLayoutSize = techDesc.Passes;
		for( UINT idx = 0; idx < techDesc.Passes; idx ++ )
		{
			D3DX11_PASS_DESC PassDesc;
			HR(Technique->GetPassByIndex( idx )->GetDesc( &PassDesc ));

			ERR(Globals::Device->CreateInputLayout( layout, layoutCount, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &pVertexLayout[idx]), 
				"Error creating " +shaderName + "-" +technique+ " : Does the layout match the effect layout?");
		}

		this->effectType = effectType;
	}

	void CustomEffect::PreDraw(){
		Globals::Context->IASetInputLayout( *pVertexLayout );
	}

	void CustomEffect::Draw(int primitiveCount, int vertexSize){
		// render
		Technique->GetDesc( &techDesc );
		for( UINT p = 0; p < techDesc.Passes; ++p )
		{
			Technique->GetPassByIndex( p )->Apply( 0, Globals::Context );
			for(int i = 0; i<=primitiveCount; i+=vertexSize)
				Globals::Context->Draw( vertexSize, i);
		}
	}

	void CustomEffect::Draw(int primitiveCount){
		// render
		HR(Technique->GetDesc( &techDesc ));
		for( UINT p = 0; p < techDesc.Passes; ++p )
		{
			HR(Technique->GetPassByIndex( p )->Apply( 0, Globals::Context ));
			Globals::Context->Draw( primitiveCount, 0 );
		}
	}

	void CustomEffect::DrawIndexed(int indexCount){
		// render
		for( UINT p = 0; p < techDesc.Passes; ++p )
		{
			Technique->GetPassByIndex( p )->Apply( 0, Globals::Context );
			Globals::Context->DrawIndexed( indexCount, 0, 0);
		}
	}

	void CustomEffect::DrawAuto(){		
		// render
		Technique->GetDesc( &techDesc );
		for( UINT p = 0; p < techDesc.Passes; ++p )
		{
			Technique->GetPassByIndex( p )->Apply( 0, Globals::Context );
			Globals::Context->DrawAuto();
		}
	}

	void CustomEffect::AddVariable(string variable){
		if(effectVariables.find(variable) == effectVariables.end()){
			effectVariables[variable] = effect->GetVariableByName(variable.c_str());
		}
	}

	void CustomEffect::AddTexture( string variable, string texture ){
		textureFileNames.push_back(texture);
		AddVariable(variable);
		texturesSet = false;
	}

	void CustomEffect::SetTexture(string variable, string texture){
		if(!texturesSet){
			texturesSet = true;

			//load textures
			for ( int i=0; i < (int) textureFileNames.size(); i++ )
			{
				textureSRV[textureFileNames[i]] = NULL;
				ERR(D3DX11CreateShaderResourceViewFromFile( Globals::Device, textureFileNames[i].c_str(), NULL, NULL, &textureSRV[textureFileNames[i]], NULL ), "Could not load texture: " + textureFileNames[i]);
			}
		}

		effectVariables[variable]->AsShaderResource()->SetResource(textureSRV[texture]);
	}

	void CustomEffect::SetTexture(string variable, ID3D11ShaderResourceView* texture){
		effectVariables[variable]->AsShaderResource()->SetResource(texture);
	}

	void CustomEffect::SetMatrix(string variable, XMMATRIX &matrix){
		XMMATRIX tmp = XMMatrixTranspose(matrix);
		effectVariables[variable]->AsMatrix()->SetRawValue(tmp.m, 0, 4*4*sizeof(float));                		
	}

	void CustomEffect::SetVector(string variable, XMVECTOR &vector){
		effectVariables[variable]->AsVector()->SetFloatVector(vector.vector4_f32);
	}

	void CustomEffect::SetBoolVector(string variable, BOOL *vector){
		effectVariables[variable]->AsVector()->SetBoolVector(vector);
	}

	void CustomEffect::SetIntVector(string variable, int *vector){
		effectVariables[variable]->AsVector()->SetIntVector(vector);
	}

	void CustomEffect::SetFloatVector(string variable, float *vector){
		effectVariables[variable]->AsVector()->SetFloatVector(vector);
	}

	void CustomEffect::SetBool(string variable, BOOL scalar){
		effectVariables[variable]->AsScalar()->SetBool(scalar);
	}

	void CustomEffect::SetInt(string variable, int scalar){
		effectVariables[variable]->AsScalar()->SetInt(scalar);
	}

	void CustomEffect::SetFloat(string variable, float scalar){
		effectVariables[variable]->AsScalar()->SetFloat(scalar);
	}

	void CustomEffect::CleanUp(){

		if(!effect){
			return;
		}

		if(pVertexLayout){
			for(unsigned int i = 0; i<vertexLayoutSize; i++){
				if(pVertexLayout[i])
					pVertexLayout[i]->Release();
			}
			delete [] pVertexLayout;
		}

		std::map<std::string, ID3D11ShaderResourceView*, NameComparer>::const_iterator textureItr;
		for(textureItr = textureSRV.begin(); textureItr != textureSRV.begin(); textureItr++){
			textureItr->second->Release();
			delete textureItr->second;
		}
		textureSRV.clear();

		std::map<std::string, ID3DX11EffectVariable*, NameComparer>::const_iterator variableItr;
		for(variableItr = effectVariables.begin(); variableItr != effectVariables.begin(); variableItr++){
			delete variableItr->second;
		}
		effectVariables.clear();

		textureFileNames.clear();
		textureFileNames.swap(vector<string>());

		effectVariables.swap(std::map<string, ID3DX11EffectVariable*, NameComparer>());
		textureSRV.swap(std::map<string, ID3D11ShaderResourceView*, NameComparer>());

		if(effect){
			effect->Release();
			effect = NULL;
		}
	}

	// creates the vertex buffer every time, since this geometry shader is considered a special worker for the cpu
	ID3D11Buffer* CustomEffect::WriteToGeometryShader(D3D11_PRIMITIVE_TOPOLOGY inputTopology, ID3D11Buffer* inputBuffer, int inputVertexCount, ID3D11Buffer* outputBuffer){
		CheckForGeometryShaderSupport();

		// Set IA parameters
		ID3D11Buffer* pBuffers[1];
		pBuffers[0] = inputBuffer;

		D3D11_BUFFER_DESC desc;
		inputBuffer->GetDesc(&desc);

		UINT stride[1] = { desc.ByteWidth / inputVertexCount };
		UINT offset[1] = { 0 };

		Globals::Context->IASetVertexBuffers( 0, 1, pBuffers, stride, offset );
		Globals::Context->IASetPrimitiveTopology( inputTopology );

		// Point to the correct output buffer
		pBuffers[0] = outputBuffer;
		Globals::Context->SOSetTargets( 1, pBuffers, offset );

		PreDraw();
		Draw(inputVertexCount); 

		// Get back to normal
		pBuffers[0] = NULL;
		Globals::Context->SOSetTargets( 1, pBuffers, offset );

		//std:swap(inputBuffer, outputBuffer);

		return outputBuffer;
	}

}


